/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */


wiwannN = function(props) {wiwannN.construct(props)} ;

wiwannN.prototype = {
    targetsInfos : [] ,
    fixed : false ,
    env : null ,
    memory : [],
    quality : null ,
    lifePhase : null ,
    type : null , //0 : neurone, 1 : stimuli , 2 acteur


    construct : function(jsonProps) {
	this.targetsInfos = [] ;
	this.memory = [] ;
	this.quality = 0.5 ;
	this.lifePhase = 0 ;
	if(jsonProps) for ( var cle in jsonProps ) {
	    this[cle] = jsonProps[cle];
	}
	this.env.registerWN(this);
	//XXX: date de creation ? temps d'exercice ?
    },

    isActor : function() {
	return( this.type == 2 ) ;
    },

    isSensor : function() {
	return( this.type == 1 ) ;
    },

    canHaveChild : function() {
	if( this.isActor()) return false ;
	return true ;
    },

    hasMaximumSize : function () {
	return this.targetsInfos.length >= this.env.getMaxSynapses() ;
    },


    canLearn : function () {
	if( this.isSensor() ) return false ;
	return true ;
    },

    canTeach : function () {
	if( this.isActor()) return false ;
	return true ;
    },

    learn : function() {
	this.env.handleLearnAsking(this);
    },

    teach : function() {
	this.env.handleTeachAsking(this);
    },

    lifeCycle : function()
    {

	//FIXME : l'algo ne va pas là, car il faut que ça reste bloqué tant que pas fini

	if(this.isMaximumSize())
	    return ;
	this.lifePhase = (this.lifePhase + 1 ) % 3 ;

	switch (this.lifePhase) {
	    case 0:
		//apprentissage
		if(this.canLearn()) {
		    this.learn();
		}
		else {
		    this.lifeCycle();
		}
		break;
	    case 1:
		//enseignement
		if(this.canTeach()) {
		    this.teach();
		}
		else {
		    this.lifeCycle();
		}
		break;
	    case 2:
		//childmaking
		if(this.canHaveChild())
		{
		    this.makeChild();
		}
		else {
		    this.lifeCycle();
		}
		break;
	}
    },



    checkUp : function() { 
	//XXX: nettoyer les synapses inutiles ?
    } ,





    makeChild : function() {
	//Dans le cerveau humain les neurones sont fabriqués dans une zone particulieres, puis ils suivent en freestyle les
	//grands axes neurologiques pour se placer. Des indices moléculaires les aident.
	//Selon moi, on est déjà dans le "plan de construction", et pas dans l'apprentissage
	//L'idée du réseau grossier déjà en place et qui s'approfondit est cela dit interessante.
	//
	//Idée: créer un enfant plus loin dans le réseau (dichotomie progressive et localisée du réseau )
	//  qui hérite de l'influx en tant que pusation d'apprenstissage sur une de ses synapse (le seuil d'excitation doit donc etre de 1)
	//   , et d'une cible, pour chaque cible


	//On choisit aléatoirement sur quel chemin créer le nouveau neurone. L'aléa est pondéré par la qualité du neurone.

	var totalQuality = 0 ;
	this.targets.each(function (targetInfos) {
	    totalQuality += targetInfos.target.quality ;
	});

	var choice = Math.random()*totalQuality ;
	var pointer = O ;
	for(indexWay=0;indexWay<this.targets.length;indexWay++) {
	    pointer += this.targets[indexWay].target.quality ;
	    if( pointer >= choice )
		break ;
	}
	var baby = new wiwannN({
		env : this.env ,
		targetsInfos : [ this.targets[indexWay] ]
	    });

	this.targets[indexWay] = baby ;

	//XXX ds le cerveau il y a de nombreuses cibles identiques pour une source identique, cela sans doute pour créer
	//Des connexions physiquement plus solides ( à l'abri des chocs etc
	//Ici je me demande si il ne faudrait pas vérifier que la cible n'existe pas déjà


    },

    action : function () {
	//TODO prevoir un equivalent à la periode refractaire ( limiter la sollicitation . 1ms pour les neurones chmimiques  http://fr.wikipedia.org/wiki/Synapse)
	this.targets.each(function (targetsInfos) {
	    //Ici il faut créer un autre thread
	    //TODO passer par une fonction env
	    targetsInfos.target.poke(targetsInfos.polarity);
	});
	//TODO : prévenir l'environnement également, si thisOb on se propose comme source.
	//TODO : si on recherche des sources, utiliser ce stimuli comme la pulsation "photographie étoile"
	//TODO : dans le cas d'un actor, cela doit provoquer qqchose de materiel
	//TODO : lancer le cycle de vie ici ?
    },

    /*
     *
     *
     *Les neurones n'ont pas tous le même seuil d'excitation de telle sorte qu'un stimulus qui déclencherait un influx nerveux
     *dans un neurone ne le déclenche pas nécessairement dans tous les neurones. Certains neurones sont très sensibles alors
     *que d'autres le sont beaucoup moins. De plus, l'influx nerveux ne se déplace pas à la même vitesse dans tous les neurones.
     * D'une façon générale, plus l'axone d'un neurone est gros, plus le seuil d'excitation du neurone est bas et plus vite
     *  se déplace l'influx nerveux le long de ce neurone
     *source  http://cours.cegep-st-jerome.qc.ca/101-902-m.f/bio903/Nerveux/influxnerveuxsuite.htm
     *
     *Note: Bizarre : plus il y a de synapses, plus le voltage monte facilement, le seuil devrait être plus haut ??
     */
    getThreshold : function() {
	//TODO: introduire la notion de qualité du neurone. Plus un neurone est de "qualité", plus le chiffre renvoyé tend vers 1
	return Math.min(2, this.targetsInfos.length) ;
    },

    poke : function(polarity) {
	thisObject = this ;
	this.memory.queue({date : date.now() , polarity : polarity});
	//On nettoie la mémoire trop ancienne
	var resultat = 0 ;
	wiwannEnv.cleanMemory(this.memory,this.env);
	for(var i = 0; i < this.memory.length ; i++)
	{
	    resultat += event.polarity ;
	    //Il y a décision si , pour une période donnée, le seuil d'excitation est atteint
	    if( resultat > thisObject.getThreshold() )
	    {
		thisObject.action();
		return ;
	    }
	}
    }


}


wiwannI = {
    wiwannN : null ,
    frequency : null
}

wiwannEnv = function(props) {wiwannEnv.construct(props)} ;

wiwannEnv = {
    tabWN : [] , //XXX : index de toutes les wn existante. En production, ce tableau devra être stocké sur le disque dur
    tabMemory : [],
    nbSensors : O ,

    neuralActionWindow : 5 , //Fenetre de duree du potentiel d'action virtuel
    //Distance ?



    construct : function(jsonProps) {
	this.tabWN = [] ;
	this.tabMemoryAskingSources = [] ;
	if(jsonProps) for ( var cle in jsonProps ) {
	    this[cle] = jsonProps[cle];
	}

    },


    //Suppression des neurones inutiles &  libération de ressources
    //Idée: fusion (2 neur qui font la même chose) & fission (1 neur à l'intérieur une entité qui fait autre choz) & simplification des réseau environnants si redondances cycliques à distance +1
    grimReaper : function()
    {

    },

    registerWN : function(wn)
    {
	this.tabWN.push(wn);
	if(wn.isSensor())
	    this.nbSensors ++ ;
    },

    getMaxSynapses : function()
    {
	return this.nbSensors ;
    },


    handleTeachAsking : function(wn)
    {
	//TODO

	//Un neurone en train d'apprendre se demande ce qui a du sens avec sa pulsation.

	//Qqq chose qui pulse en même temps que lui est reliée en polarité +
	//QQchose qui est trs souvent en pulsation, sauf quand lui pulse ou il est systématiquement éteint, est relié en polarité -

	//idéalement : pour toutes les neurones en train d'apprendre, leur envoyer un signal afin qu'elles imprimment '


	//pour chaque enseignant, leur attribuer deux notes.
	//Une note dans le négatif
	//Une note dans le positif


	//A chaque fois qu'un eleve déclenche une action, pendant la fenetre d'ouverture de décision qui suit, on va consigner tous les professeurs qui bippent aussi.
	//A la fin de la période d'apprentissage ( n bips ? ) qui doit être suffisante
	//On regarde si des professeurs + ont atteint le score requis minimal pour se lier
	//On regarde parmi les professeurs enregistrés ceux qui n'ont jamais parlé
	//XXX comme on ne sait pas par avance quels seuils doivent être choisi, il faut attribuer une note à la connexion, de façon à ce qu'à terme seules les meilleurs persistent

    },

    handleLearnAsking : function(wn)
    {
	wiwannEnv.cleanMemory(tabMemory,2000);
	this.tabMemory.push({event: date.now() , pevent: wn });
    }


}

//XXX en C++ ceci sera remplacé par un objet "memory" qui sera une liste chainée
//TODO : liste chainée avec notion d'antériorité relative au suivant ou précédent ( pour ne pas se triballer de gros datetime avec ms )
//On la parcours en partant de la fin pour savoir quoi effacer
//On a une mémoire limitée en cases de maniere à ce qu'elle reste fixe
wiwannEnv.cleanMemory = function(memoryTab,delai) {
    for(var i = 0; i< memoryTab.length;i++) {
	event = memoryTab[i];
	//TODO recuperer un objet date
	//TODO un while serait plus elegant ici
	//TODO optimise l'instanciation systématique de date
	if(event.date.earlierThan(date.now.addMs(-1 * delai)))
	{
	    memoryTab.shift();
	}
	else
	    break ;
    }
}